#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

class PerspectiveCamera {
public:
    PerspectiveCamera() { update_matrix(); };
    ~PerspectiveCamera(){};

    void update_view_matrix() {
        if (!updated) {
            up = glm::normalize(glm::cross(right, front));
            viewMatrix = glm::lookAt(position, position + front, up);
            viewProjectionMatrix = projectionMatrix * viewMatrix;
            updated = true;
        }
    }
    void update_projection_matrix() {
        if (!updated) {
            projectionMatrix = glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);
            viewProjectionMatrix = projectionMatrix * viewMatrix;
            updated = true;
        }
    }
    void update_matrix() {
        if (!updated) {
            up = glm::normalize(glm::cross(right, front));
            viewMatrix = glm::lookAt(position, position + front, up);
            projectionMatrix = glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);
            viewProjectionMatrix = projectionMatrix * viewMatrix;
            updated = true;
        }
    };

public:
    void look_at(const glm::vec3 &target) {
        updated = false;
        front = glm::normalize(target - position);
        right = glm::normalize(glm::cross(front, glm::vec3(0.0f, 1.0f, 0.0f)));
        update_view_matrix();
    }
    void set_position(const glm::vec3 &pos) {
        updated = false;
        position = pos;
        update_view_matrix();
    }
    void set_fov(float new_fov) {
        updated = false;
        fov = new_fov;
        update_projection_matrix();
    }
    void set_aspect_ratio(float ratio) {
        updated = false;
        aspectRatio = ratio;
        update_projection_matrix();
    }
    void set_near_plane(float nearp) {
        updated = false;
        nearPlane = nearp;
        update_projection_matrix();
    }
    void set_far_plane(float farp) {
        updated = false;
        farPlane = farp;
        update_projection_matrix();
    }

public:
    glm::vec3 position{0.0f};           // Camera position
    float fov = 45.0f;                  // Field of view in degrees
    glm::vec3 front{0.0f, 0.0f, -1.0f}; // Front vector
    float aspectRatio = 4.0f / 3.0f;    // Aspect ratio
    glm::vec3 up{0.0f, 1.0f, 0.0f};     // Up vector
    float nearPlane = 0.1f;             // Near clipping plane
    glm::vec3 right{1.0f, 0.0f, 0.0f};  // Right vector
    float farPlane = 1000.0f;           // Far clipping plane

public:
    glm::mat4 viewMatrix;
    glm::mat4 projectionMatrix;
    glm::mat4 viewProjectionMatrix;

private:
    bool updated = false;
};